/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       windows_game.h

	$Header: /game/windows_game.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

extern FILE*	output_file;

#include "windows_game.h"

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <windows.h>
#include <mmsystem.h>

#include <direct.h>
#include "adventure_sounds.h"
#include "convert_16_bit.h"
#include "debug_message.h"
#include "direction.h"
#include "elements_of.h"
#include "game_time.h"
#include "game_window.h"   //dsm temp
#include "mouse_event.h"
#include "operating_system_interface.h"
#include "owned_ptr.h"
#include "platform.h"
#include "resource.h"
#include "screen_rect_conversion.h"
#include "sized_int_types.h"
#include "sound_cache.h"
#include "paint_surface.h"
#include "music.h"


// all of the following globals are local to this module.  All are essentially 
// data members for t_windows_game which we do not wish to make visible in the header.
static t_windows_game*      g_game = 0;
static HINSTANCE            g_application_instance = 0;	// current instance
static HWND                 g_main_window_handle = 0;	// handle of main window.
static int                  g_show_command = 0;
static bool                 g_mouse_captured = false;

static t_memory_bitmap16_ptr  g_windowed_draw_buffer;
static t_memory_bitmap16_ptr  g_windowed_back_buffer;
static bool                 g_changing_resolutions = false;
static bool					g_window_minimized = false;
static bool					g_force_resolution = false;
static t_screen_point       g_back_buffer_size(0,0);   // size of the back buffer.
static t_screen_point		g_original_desktop_size;
static int					g_original_bitdepth = 16;

static const int k_minimum_timmer_resolution = 1;		// minimum timmer resolution we want for timeGetTime() : in milliseconds

static HDC	g_dc;
int	g_window_width = 1024;
int	g_window_height = 768;

// -------------------------------------------------------------
// -------------------------------------------------------------

HWND t_operating_system_interface::get_game_windows_handle()
{
	return g_main_window_handle;
}


HINSTANCE t_operating_system_interface::get_game_windows_instance()
{
	return g_application_instance;
}

// -----------------------------------------------------------------------
// input event interception declarations
// -----------------------------------------------------------------------
namespace
{
	t_mouse_event_handler g_left_button_down_handler;
	t_mouse_event_handler g_left_button_up_handler;
	t_mouse_event_handler g_left_button_double_clicked_handler;
	t_mouse_event_handler g_right_button_down_handler;
	t_mouse_event_handler g_right_button_up_handler;
	t_mouse_event_handler g_right_button_double_clicked_handler;
}

void set_left_button_down_handler( t_mouse_event_handler const handler )
{
	g_left_button_down_handler = handler;
}

void set_left_button_up_handler( t_mouse_event_handler const handler )
{
	g_left_button_up_handler = handler;
}

void set_left_button_double_clicked_handler( t_mouse_event_handler const handler )
{
	g_left_button_double_clicked_handler = handler;
}

void set_right_button_down_handler( t_mouse_event_handler const handler )
{
	g_right_button_down_handler = handler;
}

void set_right_button_up_handler( t_mouse_event_handler const handler )
{
	g_right_button_up_handler = handler;
}

void set_right_button_double_clicked_handler( t_mouse_event_handler const handler )
{
	g_right_button_double_clicked_handler = handler;
}

void set_left_button_handlers( t_mouse_event_handler const& down_handler, t_mouse_event_handler const& up_handler, t_mouse_event_handler const& double_handler )
{
	g_left_button_down_handler = down_handler;
	g_left_button_up_handler = up_handler;
	g_left_button_double_clicked_handler = double_handler;
}

void set_right_button_handlers( t_mouse_event_handler const& down_handler, t_mouse_event_handler const& up_handler, t_mouse_event_handler const& double_handler )
{
	g_right_button_down_handler = down_handler;
	g_right_button_up_handler = up_handler;
	g_right_button_double_clicked_handler = double_handler;
}

void get_left_button_handlers( t_mouse_event_handler& down_handler, t_mouse_event_handler& up_handler, t_mouse_event_handler& double_handler )
{
	down_handler = g_left_button_down_handler;
	up_handler = g_left_button_up_handler;
	double_handler = g_left_button_double_clicked_handler;
}

void get_right_button_handlers( t_mouse_event_handler& down_handler, t_mouse_event_handler& up_handler, t_mouse_event_handler& double_handler )
{
	down_handler = g_right_button_down_handler;
	up_handler = g_right_button_up_handler;
	double_handler = g_right_button_double_clicked_handler;
}

// -------------------------------------------------------------
// -------------------------------------------------------------
// -------------------------------------------------------------

HWND get_main_window_handle()
{
	return g_main_window_handle;
}

// -------------------------------------------------------------
// create a mouse event from word, long params
// -------------------------------------------------------------
static t_window* create_mouse_event( HWND window_handle, WPARAM word_param, LPARAM long_param, 
									 t_mouse_event& event )
{
	POINT point;
	
	point.x = LOWORD(long_param);
	point.y = HIWORD(long_param);
	// ClientToScreen( window_handle, &point );

	event.screen_point.x = point.x;
	event.screen_point.y = point.y;
	event.control_key = (word_param & MK_CONTROL) != 0;
	event.left_button = (word_param & MK_LBUTTON) != 0;
	event.middle_button = (word_param & MK_MBUTTON) != 0;
	event.right_button  = (word_param & MK_RBUTTON) != 0;
	event.shift_key     = (word_param & MK_SHIFT) != 0;

	t_window* window = t_window::get_mouse_window( event.screen_point );

	if (window != 0)
		event.client_point = window->to_client( event.screen_point );
	return window;
}

// -------------------------------------------------------------
// handle a mouse movement event.
// -------------------------------------------------------------
static bool g_send_drag_event = false;

static void mouse_move( HWND window_handle, WPARAM word_param, LPARAM long_param )
{
	t_mouse_event event;

	t_game_application::close_help_balloon();

	t_window* window = create_mouse_event( window_handle, word_param, long_param, event );

	// check if the mouse is in our main window.
	RECT window_rect;
	GetWindowRect( window_handle, &window_rect );
	t_screen_rect rect = from_windows_rect( window_rect );

	POINT point;
	point.x = event.screen_point.x;
	point.y = event.screen_point.y;

	if ( is_point_in_rect( event.screen_point, rect ) && ::WindowFromPoint( point ) == window_handle )
	{
		if (!g_mouse_captured)
		{
			SetCapture( window_handle );
			g_mouse_captured = true;
			t_game_application::update_cursor();
		}
	}
	else
	{
		if (g_mouse_captured)
		{
			ReleaseCapture();
			g_mouse_captured = false;
			t_game_application::update_cursor();
		}
	}

	g_game->mouse_move( window, event );
	if (g_send_drag_event && window != 0)
		window->drag_event( event );
	g_send_drag_event = false;
}

// -------------------------------------------------------------
// handle a left-down event
// -------------------------------------------------------------
static void left_button_down( HWND window_handle, WPARAM word_param, LPARAM long_param )
{
	t_mouse_event event;
	t_window_ptr  window = create_mouse_event( window_handle, word_param, long_param, event );

	t_game_application::close_help_balloon();

	bool result = false;
	g_left_button_down_handler( &event, &result );
	if( result )
		return;

	if (window != 0)
		window->left_button_down( event );
	g_send_drag_event = true;
}

// -------------------------------------------------------------
// handle a left-up event
// -------------------------------------------------------------
static void left_button_up( HWND window_handle, WPARAM word_param, LPARAM long_param )
{
	t_mouse_event event;
	t_window_ptr  window = create_mouse_event( window_handle, word_param, long_param, event );

	// Once the left button comes up we don't want mouse moves to generate drag events anymore
	g_send_drag_event = false;

	t_game_application::close_help_balloon();

	bool result = false;
	g_left_button_up_handler( &event, &result );
	if( result )
		return;
	
	if (window == 0)
		t_window::cancel_drag();
	else
	{
		window->accept_drag( event );
		window->left_button_up( event );
	}
}

// -------------------------------------------------------------
// handle a double-click event
// -------------------------------------------------------------
static void left_double_click( HWND window_handle, WPARAM word_param, LPARAM long_param )
{
	t_mouse_event event;
	t_window_ptr  window = create_mouse_event( window_handle, word_param, long_param, event );

	bool result = false;
	g_left_button_double_clicked_handler( &event, &result );
	if( result )
		return;
	
	if (window != 0)
		window->left_double_click( event );

}

// -------------------------------------------------------------
// handle a right-up event
// -------------------------------------------------------------
static void right_button_down( HWND window_handle, WPARAM word_param, LPARAM long_param )
{
	t_mouse_event event;
	t_window_ptr  window = create_mouse_event( window_handle, word_param, long_param, event );

	t_game_application::close_help_balloon();

	bool result = false;
	g_right_button_down_handler( &event, &result );
	if( result )
		return;
	
	if (window != 0)
		window->right_button_down( event );
}

// -------------------------------------------------------------
// handle a right-up event
// -------------------------------------------------------------
static void right_button_up( HWND window_handle, WPARAM word_param, LPARAM long_param )
{
	t_mouse_event event;
	t_window_ptr  window = create_mouse_event( window_handle, word_param, long_param, event );

	t_game_application::close_help_balloon();

	bool result = false;
	g_right_button_up_handler( &event, &result );
	if( result )
		return;
	
	if (window != 0)
		window->right_button_up( event );
}

// -------------------------------------------------------------
// handle a right-up event
// -------------------------------------------------------------
static void right_double_click( HWND window_handle, WPARAM word_param, LPARAM long_param )
{
	t_mouse_event event;
	t_window_ptr  window = create_mouse_event( window_handle, word_param, long_param, event );

	t_game_application::close_help_balloon();

	bool result = false;
	g_right_button_double_clicked_handler( &event, &result );
	if( result )
		return;
	
	if (window != 0)
		window->right_double_click( event );
}

// ---------------------------------------------------------------
// redraw windows, if required.
// ---------------------------------------------------------------
void redraw_windows()
{
	if (!t_window::painting_needed())
		return;

	if (g_main_window_handle == NULL)
		return;

	RECT rc_wnd;
	::GetClientRect(g_main_window_handle, &rc_wnd);
	::InvalidateRect(g_main_window_handle, &rc_wnd, FALSE);

// 	paint_windows();
}

// ---------------------------------------------------------------
// paint windows, if required.
// ---------------------------------------------------------------
void paint_windows()
{
	if (!t_window::painting_needed())
		return;

	t_window::paint_windows();

	// fill out the bitmap info
	BITMAPV4HEADER bmp_header;
	bmp_header.bV4Size = sizeof(bmp_header);
	bmp_header.bV4Width = g_window_width;
	bmp_header.bV4Height = -g_window_height;
	bmp_header.bV4Planes = 1;
	bmp_header.bV4BitCount = 16;
	bmp_header.bV4V4Compression = BI_BITFIELDS;
	bmp_header.bV4SizeImage = 0;
	bmp_header.bV4XPelsPerMeter = 0;
	bmp_header.bV4YPelsPerMeter = 0;
	bmp_header.bV4ClrUsed = 0;
	bmp_header.bV4ClrImportant = 0;
	bmp_header.bV4RedMask = g_red_16_bit_mask;
	bmp_header.bV4GreenMask = g_green_16_bit_mask;
	bmp_header.bV4BlueMask = g_blue_16_bit_mask;
	::StretchDIBits(g_dc, 0, 0, g_window_width, g_window_height, 
		0, 0, g_windowed_back_buffer->get_width(), g_windowed_back_buffer->get_height(), g_windowed_back_buffer->get_data_ptr(), 
		(LPBITMAPINFO)&bmp_header, DIB_RGB_COLORS, SRCCOPY);

} 

// -------------------------------------------------------------
// process a single windows message 
// -------------------------------------------------------------

static bool process_one_windows_message()
{
	MSG      msg;

	// Check the windows message queue
	if (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
	{		
		switch ( msg.message )
		{
		case WM_LBUTTONDOWN:
		case WM_MBUTTONDOWN:
		case WM_RBUTTONDOWN:
			if ( g_main_window_handle != 0 && ::GetActiveWindow() != g_main_window_handle )
				::SetActiveWindow( g_main_window_handle );
			break;
			
		case WM_QUIT:
			g_game->exit( msg.wParam );
			break;
		}

		TranslateMessage(&msg);
		DispatchMessage(&msg);
		
		return true;
	}

	return false;
}

// ---------------------------------------------------------------
// windows message handler
// ---------------------------------------------------------------
static bool g_clearing_messages = false;


static LRESULT CALLBACK window_handler( HWND window_handle, UINT message, 
								        WPARAM word_param, LPARAM long_param )
{
	bool result = false;

	switch (message) 
	{
    case WM_SYSCOMMAND:
			{
				//the application doesn't seem to process sc_close correctly anymore. force an 
				//exit of the game here. -ddn
				if (word_param == SC_CLOSE)
				{	
					t_game_application::exit(0);
					PostQuitMessage(0);
					return 0;
				}
			}
			break;

		case WM_COMMAND:
			if (g_clearing_messages)
				break;
			if ( t_window::get_modal_window())
			{
				if (t_window::get_modal_window()->menu_click( LOWORD( word_param )))
					return 0;
			}
			else if ( t_window::get_main_window() )
			{
				if ( t_window::get_main_window()->menu_click( LOWORD( word_param ) ) )
					return 0;
			}
			else if ( g_game && g_game->menu_click( LOWORD(word_param) ))
				return 0;
			break;

		case WM_DESTROY:
			t_game_application::exit(0);
			PostQuitMessage(0);
			return 0;

		case WM_LBUTTONDBLCLK:
			if (g_clearing_messages)
				break;

			left_double_click( window_handle, word_param, long_param );
			return 0;

		case WM_LBUTTONDOWN:
			if (g_clearing_messages)
				break;

			left_button_down( window_handle, word_param, long_param );
			return 0;

		case WM_LBUTTONUP:
			if (g_clearing_messages)
				break;

			left_button_up( window_handle, word_param, long_param );
			return 0;

		case WM_MOVE:
			// handle_move( LOWORD(long_param), HIWORD(long_param) );
			return 0;

		case WM_MOUSEMOVE:
			mouse_move( window_handle, word_param, long_param );
			return 0;
 
		case WM_PAINT:
		{
			PAINTSTRUCT   ps;
			HDC           device_context = BeginPaint(window_handle, &ps);
			t_screen_rect rect = from_windows_rect( ps.rcPaint );
			t_window::invalidate_main_window( rect );
			g_dc = device_context;
			paint_windows();
			EndPaint(window_handle, &ps);			
			return 0;
		}

		case WM_RBUTTONDBLCLK:
			if (g_clearing_messages)
				break;
			right_double_click( window_handle, word_param, long_param );
			return 0;
			
		case WM_RBUTTONDOWN:
			if (g_clearing_messages)
				break;
			right_button_down( window_handle, word_param, long_param );
			return 0;

		case WM_RBUTTONUP:
			if (g_clearing_messages)
				break;
			right_button_up( window_handle, word_param, long_param );
			return 0;

		case WM_SIZE:
			// handle_size( word_param, LOWORD(long_param), HIWORD(long_param) );
			return 0;

		case WM_ACTIVATE:
				if ( (LOWORD(word_param) == WA_ACTIVE)  || (LOWORD(word_param) == 	WA_CLICKACTIVE)) 
				{
					set_resolution(g_window_width , g_window_height, true);
					handle_adventure_sound_minimization(false);
				}

					
				if ( LOWORD(word_param) == 	WA_INACTIVE ) 
				{
					handle_adventure_sound_minimization(true);
				}
				break;

		case WM_DISPLAYCHANGE:
			break;
   }
   return DefWindowProc(window_handle, message, word_param, long_param);
}

// Deal with all pending windows message (without doing any other game processing)
void clear_pending_messages()
{
	g_clearing_messages = true;

	while (process_one_windows_message());

	g_clearing_messages = false;
}

t_windows_game::t_windows_game( std::string const& class_name, int icon_id )
{
	m_class_name = class_name;
	m_icon_id = icon_id;	

	g_game = this;
}

// -------------------------------------------------------------
// show / hide the hardware cursor
// -------------------------------------------------------------
void set_hardware_cursor_visible( bool arg )
{
	static bool hardware_cursor_visible = true;

	if (arg == hardware_cursor_visible)
		return;
	ShowCursor( arg );
	hardware_cursor_visible = arg;
}

// -------------------------------------------------------------
// close the application
// -------------------------------------------------------------
void close_game()
{
	DestroyWindow( g_main_window_handle );
	g_main_window_handle = NULL;
}

// -------------------------------------------------------------
// do platform-specific initialization
// -------------------------------------------------------------
void initialize_platform()
{
	ShowWindow( g_main_window_handle, g_show_command);
	UpdateWindow( g_main_window_handle );
}

// -------------------------------------------------------------
// process one iteration of the game loop 
// -------------------------------------------------------------

void process_message()
{
	bool     got_message;
	t_uint32 delay;

	// Check the windows message queue
	got_message = process_one_windows_message();
	
	redraw_windows();
	
	delay = g_game->idle();

	// If the message queue was empty and the idle loop has nothing to do for a while, we can sleep
	if (!got_message) 
	{
		DWORD result = MsgWaitForMultipleObjects( 0, NULL, false, delay, QS_ALLEVENTS );
	}
}

// -----------------------------------------------------------------------
// get mouse position, in client coordinates
// -----------------------------------------------------------------------
t_screen_point get_mouse_screen_position()
{
	POINT point;

	GetCursorPos( &point );
	return t_screen_point( point.x, point.y );
}

// -----------------------------------------------------------------------
// get mouse position, in client coordinates
// -----------------------------------------------------------------------
t_screen_point get_mouse_position( t_window const* window  )
{
	POINT point;

	GetCursorPos( &point );

	if (window == 0)
		window = t_window::get_main_window();

	return window->to_client( t_screen_point( point.x, point.y ) );
}

//
//  FUNCTION: register_window_class()
//
//  PURPOSE: Registers the window class.
//
static ATOM register_window_class( HINSTANCE application_instance,
								   char const* class_name,
								   int icon_id )
{
	WNDCLASSEX class_data;
	
	HANDLE new_cursor_image_handle = LoadImage(
		application_instance,					// handle to instance
		MAKEINTRESOURCE(IDC_EMPTY_CURSOR),		// name or identifier of the image
		IMAGE_CURSOR,							// image type
		32,										// desired width
		32,										// desired height
		LR_CREATEDIBSECTION						// load options
		);

	if ( new_cursor_image_handle == NULL )
	{
#ifndef NDEBUG
		char * msg = NULL;
		// reinterpret_cast?!?!? Don't blame me, that's how FormatMessage is defined to work...
		FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
					   0, GetLastError(), 0, reinterpret_cast< char * >( &msg ), 1, NULL );
		std::string final_message = "LoadImage(cursor) failed: ";
		final_message += msg;
		final_message += "\n";
		LocalFree( msg );
		DEBUG_MESSAGE( final_message.c_str() );
#endif
		new_cursor_image_handle = LoadCursor( NULL, IDC_ARROW );
	}

	class_data.cbSize = sizeof(WNDCLASSEX); 

	class_data.style		 = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	class_data.lpfnWndProc	 = (WNDPROC)window_handler;
	class_data.cbClsExtra	 = 0;
	class_data.cbWndExtra	 = 0;
	class_data.hInstance	 = application_instance;
	class_data.hIcon		 = LoadIcon(application_instance, MAKEINTRESOURCE( icon_id ) );
	class_data.hCursor		 = (HCURSOR)new_cursor_image_handle;
	class_data.hbrBackground = 0;
	class_data.lpszMenuName	 = 0;
	class_data.lpszClassName = class_name;
	class_data.hIconSm		 = 0;

	return RegisterClassEx(&class_data);
}

t_screen_point get_desktop_size()
{
	return g_original_desktop_size;
}

void t_windows_game::create_window()
{
	register_window_class( g_application_instance, m_class_name.c_str(), m_icon_id );

	g_main_window_handle = CreateWindow( m_class_name.c_str(),
    get_main_window_caption().c_str(),
    WS_POPUP | WS_VISIBLE,
    0, 0, g_window_width, g_window_height, NULL, NULL, 
    g_application_instance, NULL);
}


// -------------------------------------------------------------
// WinMain
// -------------------------------------------------------------

int APIENTRY WinMain(HINSTANCE application_instance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       show_command)
{
	timeBeginPeriod(k_minimum_timmer_resolution);
    	
	g_application_instance = application_instance; // Store instance handle in our global variable
	g_show_command         = show_command;	

	if (g_game == 0)
		return 0;

	g_game->create_arguments_map(lpCmdLine);
    
#ifndef NDEBUG
	// This code is for debugging only and usage demonstration only
	// If the command line argument "debug" is present output all the key/value pairs to the debug window
	
	t_string_keyed_array const& command_map = g_game->get_command_line_arguments_map();
	
	if ( command_map.find( "debug" ) != command_map.end() )
	{
		t_string_keyed_array::const_iterator	pos;
		std::string								key;
		std::string								value;
		for ( pos = command_map.begin(); pos != command_map.end(); ++pos)
		{
			key = pos->first;
			value = pos->second;
			if (pos->second.length() > 0 ) 
			{
				DEBUG_MESSAGE( format_string( "key=%s value=%s \n", pos->first.c_str(), 
					pos->second.c_str()).c_str() );
			}
			else 
			{
				DEBUG_MESSAGE( format_string( "key=%s has no value \n", pos->first.c_str()).c_str() );
			}
		}
	}
#endif
	
	
	g_game->create_window();
	if (g_main_window_handle == 0)
		return 0;
	
	set_pixel_masks( 0xF800, 0x07E0, 0x001F );
	g_windowed_draw_buffer = new t_memory_bitmap16(400, 300);
	g_windowed_back_buffer = new t_memory_bitmap16(g_window_width, g_window_height);
	
	g_game->run();    
		
	timeEndPeriod(k_minimum_timmer_resolution);

	return 0;
}

// get size / position of bounding window
t_screen_rect  get_main_window_rect()
{
	return t_screen_rect(0, 0, g_window_width, g_window_height);
}

// ---------------------------------------------------------------
// read the rectangles from a direct draw clipper object.
// ---------------------------------------------------------------
void get_main_window_visible_rects( std::vector<t_screen_rect>& rects )
{
	t_window* main_window = t_window::get_main_window();
	rects.push_back( main_window->get_screen_rect() );
}

// ---------------------------------------------------------------
// paint a window
// ---------------------------------------------------------------
// paint a single window
static void paint_window( t_window* window, t_paint_surface& paint_surface, t_screen_rect const& rect )
{
	t_screen_point paint_origin;
	t_screen_point size = paint_surface.get_screen_rect().size();

	for (paint_origin.x = rect.left; paint_origin.x < rect.right; paint_origin.x += size.x)
	{
		for (paint_origin.y = rect.top; paint_origin.y < rect.bottom; paint_origin.y += size.y)
		{
			t_screen_rect paint_rect( paint_origin.x, paint_origin.y, paint_origin.x + size.x, paint_origin.y + size.y );

			paint_rect = intersection( paint_rect, rect );

			paint_surface.set_screen_rect( paint_rect );
			window->paint( paint_surface );
			paint_surface.copy_buffer();
		}
	}
}

// ---------------------------------------------------------------
// paint a window
// ---------------------------------------------------------------
// paint a single window

void paint_window( t_window* window, t_screen_point window_origin, t_screen_rect const& rect )
{
	if (rect.width() <= 0 || rect.height() <= 0)
		return;
	
	{
		int width = g_windowed_draw_buffer->get_width();
		int height = g_windowed_draw_buffer->get_height();

		t_paint_surface paint_surface( window_origin, t_screen_rect(0,0,width,height), g_windowed_draw_buffer, g_windowed_back_buffer );

		paint_window( window, paint_surface, rect );
	}
}

// ---------------------------------------------------------------
// paint a window
// ---------------------------------------------------------------
static void paint_transparency( t_window* window, t_paint_surface& paint_surface,
							    t_window_rect& region )
{
	t_screen_point paint_origin;
	t_screen_point size = paint_surface.get_screen_rect().size();
	t_screen_point window_origin = paint_surface.get_window_origin();

	for (paint_origin.x = region.left; paint_origin.x < region.right; paint_origin.x += size.x)
	{
		for (paint_origin.y = region.top; paint_origin.y < region.bottom; 
			 paint_origin.y += size.y)
		{
			t_screen_rect paint_rect( paint_origin.x, paint_origin.y, 
					                  paint_origin.x + size.x, paint_origin.y + size.y );

			paint_rect = intersection( paint_rect, region );
			paint_surface.set_screen_rect( paint_rect );
			paint_surface.set_window_origin( window_origin );
			window->paint_transparency( paint_surface, region.windows );
		}
	}
}

// ---------------------------------------------------------------
// paint a window
// ---------------------------------------------------------------
void paint_window_transparency( t_window* window, t_screen_point window_origin, 
				                t_window_rect& region )
{
	if (region.width() <= 0 || region.height() <= 0)
		return;

  {
		int width  = g_windowed_draw_buffer->get_width();
		int height = g_windowed_draw_buffer->get_height();

		t_paint_surface paint_surface( window_origin, t_screen_rect(0,0,width,height), g_windowed_draw_buffer, g_windowed_back_buffer );

		paint_transparency( window, paint_surface, region );
	}
}

// ---------------------------------------------------------------
// platform specific shut down code
// ---------------------------------------------------------------
void shut_down()
{

}


// -----------------------------------------------------------------------
// basic window class
// -----------------------------------------------------------------------
bool changing_resolutions() 
{
	return g_changing_resolutions;
}

// -----------------------------------------------------------------------
// basic window class
// -----------------------------------------------------------------------
void set_resolution( int width, int height, bool repaint )
{
	if (width == g_window_width && height == g_window_height && !g_force_resolution)
		return;

	t_window*      main_window = t_window::get_main_window();
	t_screen_point screen_size = get_desktop_size();

	g_changing_resolutions = true;
	g_window_width = width;
	g_window_height = height;
	
	main_window->set_visible( false );

	if (g_window_minimized || g_force_resolution)
	{
		g_window_minimized = false;		
		
    SetWindowLong( g_main_window_handle, GWL_STYLE, WS_POPUP | WS_VISIBLE );
    SetWindowLong( g_main_window_handle, GWL_EXSTYLE, 0 );
	}

	RECT window_rect;
	RECT screen_rect;

	GetWindowRect( g_main_window_handle, &screen_rect );

	window_rect.left = 0;
	window_rect.top = 0;
	window_rect.right = width;
	window_rect.bottom = height;	
	
  AdjustWindowRectEx( &window_rect, WS_POPUP | WS_VISIBLE, 0, 0 );

  int window_width  = window_rect.right  - window_rect.left;
  int window_height = window_rect.bottom - window_rect.top;

  int cx = ( ::GetSystemMetrics( SM_CXSCREEN ) / 2 ) - ( window_width / 2 );
  int cy = ( ::GetSystemMetrics( SM_CYSCREEN ) / 2 ) - ( window_height / 2 );

  screen_rect.top  = cy;
  screen_rect.left = cx;
  if (screen_rect.left < 0) screen_rect.left = 0;
  if (screen_rect.top < 0) screen_rect.top = 0;	

	main_window->set_visible( true );
	g_changing_resolutions = false;

	// MoveWindow(g_main_window_handle, screen_rect.left, screen_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top,  repaint );
}

void fatal_error( std::string const& message )
{
	MessageBox( 0, message.c_str(), "Fatal Error", MB_ICONSTOP | MB_OK );
	exit(0);
}


// get current directory
std::string get_current_folder()
{
	char buffer[512];

	getcwd( buffer, sizeof( buffer ));
	return buffer;
}